/**
* Copyright (C) 2013 Guangzhou Tronlong Electronic Technology Co., Ltd. - www.tronlong.com
* 
* @file rtsp_camera.cpp
* 
* @brief RTSPCamera Module: capture H264 image from IP Camera RTSP stream
*
* @author Tronlong <support@tronlong.com>
* 
* @version V1.0
* 
* @date 2023-6-21
**/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "rtsp_camera.h"

#define LAUNCH_FMT "rtspsrc location=%s ! rtph264depay ! h264parse ! video/x-h264, stream-format=byte-stream, alignment=au ! appsink name=appsink"

RTSPCamera::RTSPCamera(const char *url_) {
    url = strdup(url_);
    launch = (char *)malloc(256);
    snprintf(launch, 256, LAUNCH_FMT, url); 
}

RTSPCamera::~RTSPCamera() {
    free(url);
    free(launch);
}

bool RTSPCamera::start() {
    /* create pipeline */
    pipeline = gst_parse_launch(launch, NULL);

    /* get appsink */
    sink = (GstAppSink*)gst_bin_get_by_name(GST_BIN(pipeline), "appsink");
    gst_app_sink_set_wait_on_eos(sink, false);

    /* start pipeline */
    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    /* start to watch bus message */
    pthread_create(&tid, NULL, bus_watch, this);

    g_print("RTSPCamera: start to capture video in %s\n", url);

    return true;
}

bool RTSPCamera::stop() {
    g_print("RTSPCamera: stop\n");

    /* send end-of-stream event to pipeline */
    gst_element_send_event(pipeline, gst_event_new_eos());

    /* wait watch bus thread exit */
    pthread_join(tid, NULL);

    /* release */
    gst_object_unref(bus);
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);

    return true;
}

bool RTSPCamera::capture(uint8_t *buf, uint32_t *size) {
    GstSample *sample;
    GstBuffer *buffer;
    GstMapInfo map;

    /* get buffer */
    sample = gst_app_sink_pull_sample(sink);
    if (! sample) {
        g_error("failed to pull sample");
        return false;
    }
    buffer = gst_sample_get_buffer(sample);

    /* map buffer */
    gst_buffer_map(buffer, &map, GST_MAP_READ);

    /* get h264 segment data */
    memcpy(buf, map.data, map.size);
    *size = map.size;

    /* release */
    gst_buffer_unmap(buffer, &map);
    gst_sample_unref(sample);

    return true;
}

void *RTSPCamera::bus_watch(void *data) {
    RTSPCamera *thiz = (RTSPCamera *)data;
    GError *err;
    gchar *debug_info;

    /* get pipeline bus */
    thiz->bus = gst_element_get_bus(thiz->pipeline);

    /* wait for message */
    GstMessage *msg = gst_bus_timed_pop_filtered(thiz->bus, 
            GST_CLOCK_TIME_NONE, 
            (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));

    /* show message */
    switch (GST_MESSAGE_TYPE(msg)) {
    case GST_MESSAGE_ERROR:
        gst_message_parse_error(msg, &err, &debug_info);

        g_printerr("Error received from element %s: %s\n", GST_OBJECT_NAME(msg->src), err->message);
        g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");

        g_error("RTSPCamera: ERROR");

        g_clear_error(&err);
        g_free(debug_info);
    case GST_MESSAGE_EOS:
        g_print("RTSPCamera: end of stream\n");
    default:
        ;
    }

    gst_message_unref(msg);

    return NULL;
}

static void example_main()
{
	uint32_t h264_buf_size = params.width * params.height * 1.5;
	uint8_t *h264_buf = (uint8_t *)malloc(h264_buf_size);

	/* to fetch data form IP Camera */
	RTSPCamera camera(rtsp_url);
	camera.start();

	while (1) {
		/* fetch H264 segment data from RTSP */
		if (! camera.capture(h264_buf, &h264_buf_size)) {
			perror("capture failed");
			break;
		}
		/* decode h264 to NV21 image */
		video_decoder_decode(vdecoder, h264_buf, h264_buf_size);
	}

release:
	camera.stop(); /* release */
}

